﻿using System;
using System.IO;
using System.Net;
using Net.Event;
using Net.Share;
using Net.System;
using Net.Helper;
using Newtonsoft_X.Json;
using Cysharp.Threading.Tasks;
using System.Security.Authentication;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
#if !UNITY_EDITOR && UNITY_WEBGL
using UnityWebSocket;
#else
using WebSocketSharp;
#endif

namespace Net.Client
{
    /// <summary>
    /// web客户端类型
    /// 第三版本 2020.9.14
    /// </summary>
    [Serializable]
    public class WebClient : ClientBase
    {
        public WebSocket WSClient { get; private set; }
        /// <summary>
        /// websocket连接策略, 有wss和ws
        /// </summary>
        public string Scheme { get; set; } = "ws";
        /// <summary>
        /// Ssl类型
        /// </summary>
        public SslProtocols SslProtocols { get; set; }
        /// <summary>
        /// 证书
        /// </summary>
        public X509Certificate2 Certificate { get; set; }

        ~WebClient()
        {
#if !UNITY_EDITOR
            Close();
#endif
        }

        public override void OnSetConfigInfo(params object[] args)
        {
            Scheme = args[0].ToString();
            SslProtocols = (SslProtocols)args[1];
            var pfxPath = args[2].ToString();
            var password = args[3].ToString();
            if (string.IsNullOrEmpty(pfxPath))
                return;
            if (!File.Exists(pfxPath))
                return;
            var pfxData = File.ReadAllBytes(pfxPath);
            Certificate = new X509Certificate2(pfxData, password);
        }

        protected override void OnCreateSocket(string ipAddress, int port)
        {
#if UNITY_EDITOR || !UNITY_WEBGL
            if (ipAddress == "127.0.0.1" || ipAddress == "localhost")
                ipAddress = NetPort.GetIP();
#endif
            WSClient = new WebSocket($"{Scheme}://{ipAddress}:{port}/");
#if UNITY_EDITOR || !UNITY_WEBGL
            if (Scheme == "wss")
            {
                if (Certificate == null)
                    Certificate = CertificateHelper.GetDefaultCertificate();
                WSClient.SslConfiguration.ClientCertificates = new X509CertificateCollection { Certificate };
                WSClient.SslConfiguration.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
                WSClient.SslConfiguration.EnabledSslProtocols = SslProtocols;
            }
#endif
            WSClient.OnOpen += (sender, e) =>
            {
                var segment = BufferPool.Take(Config.SendBufferSize);
                segment.Write(PreUserId);
#if UNITY_EDITOR || !UNITY_WEBGL
                WSClient.Send(segment.ToArray(true));
#else
                WSClient.SendAsync(segment.ToArray(true));
#endif
            };
            WSClient.OnError += (sender, e) =>
            {
                NDebug.LogError(e.Exception);
            };
            WSClient.OnClose += (sender, e) =>
            {
                Connected = false;
                NetworkState = NetworkState.ConnectLost;
                MainThreadEnqueue(OnConnectLost);
                RpcQueue = new();
                NDebug.Log("websocket关闭！");
            };
            WSClient.OnMessage += (sender, e) =>
            {
                if (e.IsText)
                {
                    trafficStatistics.ReceivedBytesPerSecond += e.Data.Length * 2;
                    trafficStatistics.ReceivedPacketsPerSecond++;
                    var model = JsonConvert.DeserializeObject<MessageModel>(e.Data);
                    var model1 = RpcUnitFactory.Create(cmd: model.cmd, kernel: true, protocol: model.func.CRCU32(), pars: model.GetPars());
                    OnCommand1Handler(model1, null);
                }
                else if (e.IsBinary)
                {
                    var data = e.RawData;
                    trafficStatistics.ReceivedBytesPerSecond += data.Length;
                    trafficStatistics.ReceivedPacketsPerSecond++;
                    var buffer = BufferPool.Take(data.Length);
                    Unsafe.CopyBlock(ref buffer.Buffer[0], ref data[0], (uint)data.Length);
                    buffer.Count = data.Length;
                    ReceiveHandler(ref buffer);
                    BufferPool.Push(buffer);
                }
            };
        }

        protected override void OnBindSocketEndPoint(int localPort)
        {
        }

        protected override void OnSocketConnect(string ipAddress, int port)
        {
            WSClient.ConnectAsync(); //这里必须是Async才能在WebGL对接相同的API
        }

        protected override void SendHandshake()
        {
        }

        public override void ReceiveHandler()
        {
        }

        protected override void SendByteData(ISegment buffer)
        {
            trafficStatistics.SentBytesPerSecond += buffer.Count;
            trafficStatistics.SentPacketsPerSecond++;
#if UNITY_EDITOR || !UNITY_WEBGL
            WSClient.Send(new MemoryStream(buffer.Buffer, buffer.Offset, buffer.Count, true, true), buffer.Count);
#else
            WSClient.SendAsync(buffer.ToArray());
#endif
        }

        public override void Close(bool await = true, int millisecondsTimeout = 100)
        {
            base.Close(await, millisecondsTimeout);
            if (WSClient != null)
            {
                WSClient.CloseAsync();
                WSClient = null;
            }
        }
    }
}